/*******************************************************************************
 * Copyright 2009 DCSpectrometer - http://code.google.com/p/dcspectrometer 
 *  
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *     
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 *******************************************************************************/
package com.dcspectrometer;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

import com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam;

/** Loads RAW image and provides 16-bit access to image data. */
public class ImageLoader {

  private static final String DCRAW_DIRECTORY = "/home/user/Desktop/dcraw";
  private static final String DCRAW_COMMAND = "dcraw -T -4";
  private static final int CONVERSION_COMPLETED = 0;
  private static final int CONVERSION_FAILED = -1;
  private static final int PROCESS_COMPLETED = 0;

  private ImageLoader() {
  }

  public static BufferedImage getImage(String fileName) throws IllegalArgumentException,
      IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("Parameter fileName is null.");
    }

    File rawFile = null;
    File tiffFile = null;
    rawFile = new File(fileName);
    if (!rawFile.exists()) {
      throw new IllegalArgumentException("File does not exist.");
    }

    // Check that file is Nikon NEF
    if ((fileName.lastIndexOf(".NEF") > 0)
        && (fileName.lastIndexOf(".NEF") == fileName.length() - 4)) {
      tiffFile = new File(fileName.substring(0, fileName.length() - 4) + ".tiff");

    } else if ((fileName.lastIndexOf(".tif") > 0)
        && (fileName.lastIndexOf(".tif") == fileName.length() - 4)) {
      tiffFile = new File(fileName.substring(0, fileName.length() - 4) + ".tiff");
    } else {
      throw new IllegalArgumentException(
          "The file name is not consistent with Nikon NEF file type.");
    }

    if (fileName.lastIndexOf(".tif") > 0) {
      Process process = Runtime.getRuntime().exec(
          "cp " + fileName + " " + fileName.substring(0, fileName.length() - 4) + ".tiff", null);
      int exitStatus = -1;
      try {
        exitStatus = process.waitFor();
      } catch (Throwable ex) {
        throw new IOException("Error occurred while coping files.");
      }

      if (exitStatus != PROCESS_COMPLETED) {
        throw new IOException("Error occurred while coping files.");
      }
    } else {
      if (convertToTiff(rawFile) != CONVERSION_COMPLETED) {
        throw new IOException("Conversion process terminated unexpectedly.");
      }
    }
    // Open Tiff
    if (!tiffFile.exists()) {
      throw new IOException("Tiff file produced by dcraw does not exist.");
    }

    Iterator<ImageReader> readers = ImageIO.getImageReadersBySuffix("tiff");
    if (readers.hasNext() == false) {
      throw new IOException("Tiff reader is unavailable.");
    }

    BufferedImage image = ImageIO.read(tiffFile);
    if (image == null) {
      throw new IOException("Could not produce raster.");
    }

    return image;
  }

  /**
   * Returns DCRAW_COMPLETED if image has been properly converted.
   * 
   * @param file
   * @return
   * @throws IllegalArgumentException
   * @throws IOException
   */
  public static int convertToTiff(File rawFile) throws IllegalArgumentException, IOException {
    if (!rawFile.exists()) {
      throw new IllegalArgumentException();
    }

    // Create Process that will run DCRaw conversion
    String commandLine = DCRAW_DIRECTORY + "/" + DCRAW_COMMAND + " " + rawFile.getAbsoluteFile();
    Process process = null;
    if (DCRAW_DIRECTORY == null) {
      process = Runtime.getRuntime().exec(commandLine, null);
    } else {
      process = Runtime.getRuntime().exec(commandLine, null, new File(DCRAW_DIRECTORY));
    }

    // Wait for DCRaw to finish executing
    int exitStatus = -1;
    try {
      exitStatus = process.waitFor();
    } catch (Throwable ex) {
      throw new IOException("Error occurred while running dcraw.");
    }

    if (exitStatus == PROCESS_COMPLETED) {
      return CONVERSION_COMPLETED;
    }

    return CONVERSION_FAILED;
  }

  /**
   * Writes image into a TIFF file.
   * 
   * @param filename
   * @param image
   * @throws IOException
   */
  public static void saveTiff(String filename, BufferedImage image) throws IOException {
    File tiffFile = new File(filename);
    ImageOutputStream ios = null;
    ImageWriter writer = null;

    // find an appropriate writer
    Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName("TIF");
    if (it.hasNext()) {
      writer = (ImageWriter) it.next();
    } else {
      throw new IOException("No TIF writer found.");
    }

    // setup writer
    ios = ImageIO.createImageOutputStream(tiffFile);
    writer.setOutput(ios);
    TIFFImageWriteParam writeParam = new TIFFImageWriteParam(Locale.ENGLISH);
    writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    // see writeParam.getCompressionTypes() for available compression type strings
    writeParam.setCompressionType("PackBits");

    // convert to an IIOImage
    IIOImage iioImage = new IIOImage(image, null, null);

    // write it!
    writer.write(null, iioImage, writeParam);
  }

}
